/**************************************************************************************
*  srtj.c
*
*  This file defines native code main() function as well as additional code
*  supporting the simpleRTJ run-time requirements.
*
**************************************************************************************
*
* This file is covered by the GNU GPL with the following exception:
*   As a special exception, the copyright holders of this library give you permission
*   to link this library with independent modules to produce an executable, regardless
*   of the license terms of these independent modules, and to copy and distribute the
*   resulting executable under terms of your choice, provided that you also meet, for
*   each linked independent module, the terms and conditions of the license of that
*   module. An independent module is a module which is not derived from or based on
*   this library. If you modify this library, you may extend this exception to your
*   version of the library, but you are not obligated to do so. If you do not wish
*   to do so, delete this exception statement from your version.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY,  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL RTJ COMPUTING BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Copyright (c) 2000-2002 RTJ Computing Pty. Ltd. All rights reserved.
**************************************************************************************/
#include "config.h"
#include "general.h"
#include <lpc2xxx.h>
#include "uart.h"
#include "startup/config.h"
#include "startup/irq_tc.h"
#include "std_c.h"
#include "javavm.h"
#include "jvm.h"

#define MAX_JAPPS       12
#define JAPPS_START     0x20000
#define FLASH_END       0x80000     // for LPC2148

volatile uint32 systemTimeMs;
void (*pTimerCallback)(void);
void initTimer0(void (*pISR)(), void (*pCallback)());
void tc0Handler(void);

extern void setJavaConsoleUart(uart_t *u);
uart_t uart0, uart1;
uint32 japps_loc[MAX_JAPPS];

//-------------------------------- simpleRTJ declarations ---------------------------------
// Default VM definitions
#define DEF_TIME_SLICE      1          /* milliseconds */
#define DEF_HEAP_SIZE       0x6000
#define DEF_REFS_COUNT		600
#define DEF_STRINGS_COUNT	100
#define DEF_THREADS         8
#define DEF_TIMERS          4

#if ENABLE_REMDBG
// Serial ports debugging
extern bool openDbgPort(void);
extern void closeDbgPort(void);
extern bool dbgReq(bool get, void *buf, int16 len);

int loadDbgParam(void);
#endif
void loadVMSettings(void);

/* Event counters */
enum evEvents
{
    EV_RESERVED,        /* location at zero is reserverd for software timers (handled internally by VM) */
    EVENTS_COUNT
};
static uint16 event_counters[EVENTS_COUNT];
uint32 vm_time_slice, vm_heap_size, vm_refs, vm_threads, vm_strings, vm_timers;

// java heap memory will be allocated from the system global memory
uint8 heapmem[DEF_HEAP_SIZE];
uint8 *appmem;

vm_config_t vmconfig;
static int16 args_count = 0;
static int8 **args = NULL;

extern native_func_ptr native_tbl[];
extern void vmStart(vm_config_t *cfg);
extern void clearMem(uint8 *mem, int32 len);
void notify(uint16 code, char *msg);
int8 *getProperty(int8 *key);
void vm_notify(uint16 code, char *msg);
//------------------------------------------------------------------------------------

/* --------------------------------------------------------------------- */
int main(int argc, char* argv[])
{
    char bf[16], ch;
    int i, j, p, japps;
    app_t *app;
#if ENABLE_REMDBG
    bool dbg;
#endif

    memSet(&vmconfig, 0, sizeof(vm_config_t));

    initTimer0(timer0ISR, tc0Handler);
    uart0.uartRegBase = UART0_REG_BASE;
    initUart(&uart0, B38400((CRYSTAL_FREQUENCY * PLL_FACTOR) / VPBDIV_FACTOR), UART_8N1, UART_FIFO_OFF);

    setJavaConsoleUart(&uart0);

    uartSendCh(&uart0, '\n');
#if ENABLE_REMDBG
    uartSendString(&uart0, "simpleRTJ - with JVM debugging enabled\n");
#else
    uartSendString(&uart0, "simpleRTJ - without JVM debugging\n");
#endif
    
    loadVMSettings();
#if 1
    uartSendString(&uart0, "Time slice: ");
    uartSendString(&uart0, int2Str(bf, vm_time_slice, 4, '0', true));
    uartSendCh(&uart0, '\n');
    uartSendString(&uart0, "Heap:       0x");
    uartSendString(&uart0, toHex8(vm_heap_size, bf));
    uartSendCh(&uart0, '\n');
    uartSendString(&uart0, "References: ");
    uartSendString(&uart0, int2Str(bf, vm_refs, 4, '0', true));
    uartSendCh(&uart0, '\n');
    uartSendString(&uart0, "Strings:    ");
    uartSendString(&uart0, int2Str(bf, vm_strings, 4, '0', true));
    uartSendCh(&uart0, '\n');
    uartSendString(&uart0, "Threads:    ");
    uartSendString(&uart0, int2Str(bf, vm_threads, 4, '0', true));
    uartSendCh(&uart0, '\n');
    uartSendString(&uart0, "Sw. timers: ");
    uartSendString(&uart0, int2Str(bf, vm_timers, 4, '0', true));
    uartSendCh(&uart0, '\n');
    uartSendCh(&uart0, '\n');
#endif
    while (1){
        uint32 pmem = JAPPS_START;
        // list all java applications
        japps = 0;
        for (i=0, j=0; i < MAX_JAPPS && pmem < FLASH_END; i++, pmem += 0x8000) {
            app = (app_t *)pmem;
            if (app->magic == MAGIC) {
                uartSendCh(&uart0, 'A' + j);
                uartSendString(&uart0, " - ");
                for (p=0; p < sizeof(app->desc); p++) {
                    ch = app->desc[p];
                    if (ch == 0)
                        break;
                    uartSendCh(&uart0, ch);
                }
                while (p++ < 38) 
                    uartSendCh(&uart0, ' ');
                uartSendString(&uart0, toHex8(pmem, bf));
                uartSendCh(&uart0, '\n');
                japps_loc[j++] = pmem;
                japps++;
            }
        }
        uartSendCh(&uart0, '\n');
        if (japps == 0) {
            uartSendString(&uart0, "NO JAVA APPLICATIONS FOUND");
            while (true) ;
        }

        uartSendString(&uart0, "Enter letter of a Java program to run: ");
        while (true) {
            ch = uartGetCh(&uart0);
            if (ch >= 'a') 
                ch -= 0x20;
            j = ch - 'A';
            if (j >= 0 && j < japps) {
                uartSendChar(&uart0, ch);
                uartSendChar(&uart0, '\n');
                app = (app_t *)japps_loc[j];
                break;
            }
        }
        // simpleRTJ start-up initialisation
        vmconfig.references = (uint16)vm_refs;
        vmconfig.strings = (uint16)vm_strings;
        vmconfig.threads = (uint16)vm_threads;
        vmconfig.time_slice = (uint16)vm_time_slice;
        vmconfig.app_start = (uint8 *)app;
        vmconfig.heap_start = heapmem;
        vmconfig.heap_end = (uint8 *)(heapmem + vm_heap_size);
        vmconfig.native_tbl = native_tbl;
        vmconfig.usrNotify = vm_notify;
#if ENABLE_REMDBG
        vmconfig.dbgReq = NULL;
        dbg = false;
        // only DemoDbg can be debugged in the evaluation version of simpleRTJ
        if (strEq(app->id, "DemoDbg")) {
            ch = 0;
            uartSendString(&uart0, "\nRun Java application in debug mode? [Y/N]: ");
            while (ch != 'Y' && ch != 'N') {
                ch = uartGetCh(&uart0);
                if (ch >= 'a') 
                    ch -= 0x20;
                if (ch == 'Y') 
                    dbg = true;
            }
            uartSendChar(&uart0, ch);
            uartSendChar(&uart0, '\n');
        }
        if (dbg)
        {
            if (openDbgPort())
                vmconfig.dbgReq = dbgReq;
        }
#endif
        vmconfig.events = EVENTS_COUNT;
        vmconfig.ev_counters = event_counters;
        vmconfig.timers = (uint16)vm_timers;
        vmconfig.getProperty = getProperty;
        vmconfig.args_count = args_count;
        vmconfig.args = args;
    
        uartSendString(&uart0, "Starting: ");
        i = 0;
        while (true) {
            ch = app->desc[i++];
            if (ch == 0)
                break;
            uartSendCh(&uart0, ch);
        }
        uartSendCh(&uart0, '\n');
    
        /* start java program running */
        vmStart(&vmconfig);
    
#if ENABLE_REMDBG
        if (vmconfig.dbgReq)
            closeDbgPort();
#endif
    
        uartSendString(&uart0, "GC count: ");
        uartSendString(&uart0, int2Str(bf, gc_count, 4, ' ', true));
        uartSendCh(&uart0, '\n');
        uartSendCh(&uart0, '\n');
    }

    return 0;
}

/* ---------------------------------------------------------------------
** Loads VM start-up settings from "srtj.ini". If file doesn't exist
** default settings are used instead.
--------------------------------------------------------------------- */
void loadVMSettings(void)
{
#if 0
    FILE *f;
    char buf[80], scanstr[16], *p;
    bool hex;
#endif
    // setup default run-time values
    vm_heap_size = 0;
    vm_refs = 0;
    vm_strings = 0;
    vm_threads = 0; 
    vm_timers = 0;
#if 0
    // override defaults from ini file
    f = fopen("srtj.ini", "rb");
    if (f) {
        while (!feof(f)) {
            fgets(buf, sizeof(buf) - 1, f);
            if (strlen(buf) == 0 || *buf == ';')
                continue;
            p = strchr(buf, '=');
            if (p) {
                *p++ = 0;
                hex = false;
                if (p[0] == '0' && p[1] == 'x') {
                    p += 2;
                    hex = true;
                }
                strcpy(scanstr, hex ? "%lx" : "%ld");
                if (strstr(buf, "timeslice"))
                    sscanf(p, scanstr, &vm_time_slice);
                if (strstr(buf, "heap"))
                    sscanf(p, scanstr, &vm_heap_size);
                else if (strstr(buf, "refs"))
                    sscanf(p, scanstr, &vm_refs);
                else if (strstr(buf, "strings"))
                    sscanf(p, scanstr, &vm_strings);
                else if (strstr(buf, "threads")) 
                    sscanf(p, scanstr, &vm_threads);
                else if (strstr(buf, "timers")) 
                    sscanf(p, scanstr, &vm_timers);
            }
        }
        fclose(f);
    }
#endif
    if (!vm_time_slice)
        vm_time_slice = DEF_TIME_SLICE;
    if (!vm_heap_size)
        vm_heap_size = DEF_HEAP_SIZE;
    if (!vm_refs)
        vm_refs = DEF_REFS_COUNT;
    if (!vm_strings)
        vm_strings = DEF_STRINGS_COUNT;
    if (!vm_threads)
        vm_threads = DEF_THREADS;
    if (!vm_timers)
        vm_timers = DEF_TIMERS;
}
/* --------------------------------------------------------------------- */
void vm_notify(uint16 code, char *msg)
{
	char bf[80];
	char temp[12];
    uint16 i, subcode;

    subcode = (uint16)(code & 0xFF);
    code >>= 8;

    if (subcode > NOTIFY_VM) {
    	strCpy(bf, "Notify: 0x");
    	strCat(bf, toHex2(subcode, temp));
        if (code) {
            strCat(bf, ":");
            strCat(bf, toHex2(code, temp));
        }
    	if (msg) {
    		strCat(bf, " - ");
    		strCat(bf, msg);
    	}
    	strCat(bf, "\r\n");
		uartSendString(&uart0, bf);
    }
    else
    {
        // program end? 
        if (code == 1 && subcode == 0x32) {
	    	strCpy(bf, "\r\n");
            if (msg) {
	    		strCat(bf, msg);
		    	strCat(bf, "\r\n");
	    	}
			uartSendString(&uart0, bf);
        }
        else {
	    	strCpy(bf, "Exception: 0x");
	    	strCat(bf, toHex2(code, temp));
	    	strCat(bf, ": 0x");
	    	strCat(bf, toHex2(subcode, temp));
	    	if (msg) {
	    		strCat(bf, " - ");
	    		strCat(bf, msg);
	    	}
	    	strCat(bf, "\r\n");
	        uartSendString(&uart0, bf);
	        if (vmconfig.stack_trace != NULL && vmconfig.stack_trace->count > 0) {
	        	uartSendString(&uart0, "Stack trace:\r\n");
	            for (i=0; i < (uint16)vmconfig.stack_trace->count; i++) {
	            	strCpy(bf, "Method: 0x");
			    	strCat(bf, toHex8((uint32)(vmconfig.stack_trace->trace[i].method), temp));
			    	strCat(bf, "   PC: 0x");
			    	strCat(bf, toHex4((uint32)(vmconfig.stack_trace->trace[i].pc), temp));
			    	strCat(bf, "\r\n");
					uartSendString(&uart0, bf);
	            }
	        }
	  	}
    }
}
/* --------------------------------------------------------------------- */
int8 *getProperty(int8 *key)
{
    if (strCmp(key, "file.separator") == 0)
        return "\\";
    else if (strCmp(key, "path.separator") == 0)
        return ";";
    else if (strCmp(key, "line.separator") == 0)
        return "\r\n";
    return NULL;
}
/* --------------------------------------------------------------------- */
void sysError(char *msg)
{
    uartSendString(&uart0, msg);
}
/* --------------------------------------------------------------------- */
void initTimer0(void (*pISR)(), void (*pCallback)())
{
  //store timer callback function pointer
  pTimerCallback = pCallback;
  
  //initialize VIC for Timer0 interrupts
  VICIntSelect &= ~0x10;       //Timer0 interrupt is assigned to IRQ (not FIQ)
  VICVectAddr4  = (tU32)pISR;  //register ISR address
  VICVectCntl4  = 0x24;        //enable vector interrupt for timer0
  VICIntEnable  = 0x10;        //enable timer0 interrupt
  
  //initialize and start Timer0
  T0TCR = 0x00000002;                           //disable and reset Timer0
  T0PC  = 0x00000000;                           //no prescale of clock
  T0MR0 = (CRYSTAL_FREQUENCY * PLL_FACTOR) /    //calculate no of timer ticks
          (SYSTEM_TIME_TICK * VPBDIV_FACTOR);   //for a given system tick rate
  T0IR  = 0x000000ff;                           //reset all flags before enable IRQs
  T0MCR = 0x00000003;                           //reset counter and generate IRQ on MR0 match
  T0TCR = 0x00000001;                           //start Timer0
  
  //reset system time
  systemTimeMs = 0;
}
/* --------------------------------------------------------------------- */
void tc0Handler(void)
{
 	// timebase for thread scheduler
    if (vmconfig.vmTimeSlice != NULL)
	    vmconfig.vmTimeSlice();
	    
	// VM real-time event triggering can be hadled from here or from the appropriate C interrupt
	// function using the code below:
	//		if (vmconfig.vmUpdateEventCounter != NULL)
	//			vmconfig.vmUpdateEventCounter(EV_XXXX);
}
/* --------------------------------------------------------------------- */

